const Client = require('ftp');
const fs = require('fs');
const path = require('path');
const chalk = require('chalk');

class FTPClient {
    constructor(config) {
        this.config = config;
        this.client = new Client();
        this.isConnected = false;
    }

    async connect(forcePassive = null) {
        if (this.isConnected) {
            return true;
        }

        // 如果有旧的连接，先清理
        if (this.client) {
            this.client.removeAllListeners();
            this.client.destroy();
        }
        
        this.client = new Client();
        const passiveMode = forcePassive !== null ? forcePassive : (this.config.passive !== false);
        const modeText = passiveMode ? '被动模式' : '主动模式';
        console.log(chalk.blue(`尝试连接到 ${this.config.host}:${this.config.port || 21}... (${modeText})`));
        
        return new Promise((resolve, reject) => {
            const connectTimeout = setTimeout(() => {
                console.error(chalk.red('连接超时'));
                this.isConnected = false;
                resolve(false);
            }, 12000); // 12秒连接超时
            const passiveMode = forcePassive !== null ? forcePassive : (this.config.passive !== false);
            const timeout = this.config.timeout || 60000;
            const connectionOptions = {
                host: this.config.host,
                port: this.config.port || 21,
                user: this.config.user,
                password: this.config.password,
                secure: this.config.secure || false,
                secureOptions: {
                    rejectUnauthorized: false
                },
                passive: passiveMode,
                pasvTimeout: timeout, // 使用配置的超时时间
                connTimeout: Math.min(timeout, 30000), // 控制连接超时，最多30秒
                keepalive: 30000
            };

            if (this.config.debug) {
                console.log(chalk.gray('连接配置:', JSON.stringify({
                    ...connectionOptions,
                    password: '****'
                }, null, 2)));
            }

            this.client.on('ready', () => {
                clearTimeout(connectTimeout);
                this.isConnected = true;
                console.log(chalk.green('✓ FTP连接成功'));
                resolve(true);
            });

            this.client.on('error', (err) => {
                clearTimeout(connectTimeout);
                console.error(chalk.red('FTP连接失败:'), err.message);
                this.isConnected = false;
                resolve(false);
            });

            this.client.on('close', (hadError) => {
                this.isConnected = false;
                if (hadError) {
                    console.log(chalk.yellow('FTP连接异常关闭'));
                }
            });

            this.client.connect(connectionOptions);
        });
    }

    async disconnect() {
        return new Promise((resolve) => {
            if (this.isConnected) {
                this.client.end();
                this.client.destroy();
                this.isConnected = false;
                console.log(chalk.yellow('FTP连接已断开'));
            }
            resolve();
        });
    }

    async ensureDir(remotePath) {
        return new Promise((resolve, reject) => {
            this.client.mkdir(remotePath, true, (err) => {
                if (err && err.code !== 550) { // 550 = directory already exists
                    console.error(chalk.red(`创建远程目录失败 ${remotePath}:`), err.message);
                    resolve(false);
                } else {
                    resolve(true);
                }
            });
        });
    }

    async uploadFile(localPath, remotePath, retryCount = 0, usePassive = null) {
        const MAX_RETRIES = 3;
        
        if (!this.isConnected) {
            const connected = await this.connect(usePassive);
            if (!connected) return false;
        }

        return new Promise((resolve, reject) => {
            // 确保远程目录存在
            const remoteDir = path.dirname(remotePath);
            this.ensureDir(remoteDir).then(() => {
                const timeout = setTimeout(() => {
                    console.error(chalk.red(`上传超时 ${localPath}`));
                    this.isConnected = false;
                    resolve(false);
                }, 45000); // 45秒超时

                this.client.put(localPath, remotePath, (err) => {
                    clearTimeout(timeout);
                    
                    if (err) {
                        console.error(chalk.red(`上传失败 ${localPath}:`), err.message);
                        
                        // 如果是连接错误且还有重试次数
                        if (retryCount < MAX_RETRIES && (err.message.includes('ETIMEDOUT') || err.message.includes('connect') || err.message.includes('data connection'))) {
                            let nextMode = usePassive;
                            let modeText = '';
                            
                            // 自动切换模式：第1次重试切换到被动模式，第2次重试切换到主动模式
                            if (retryCount === 0) {
                                nextMode = true; // 切换到被动模式
                                modeText = ' (切换到被动模式)';
                            } else if (retryCount === 1) {
                                nextMode = false; // 切换到主动模式
                                modeText = ' (切换到主动模式)';
                            } else {
                                nextMode = null; // 使用默认配置
                                modeText = ' (使用默认配置)';
                            }
                            
                            console.log(chalk.yellow(`正在重试上传 ${localPath} (${retryCount + 1}/${MAX_RETRIES})${modeText}...`));
                            this.isConnected = false; // 强制重连
                            
                            setTimeout(() => {
                                this.uploadFile(localPath, remotePath, retryCount + 1, nextMode)
                                    .then(resolve)
                                    .catch(() => resolve(false));
                            }, 3000); // 等待3秒后重试
                            return;
                        }
                        
                        resolve(false);
                    } else {
                        console.log(chalk.green(`✓ 上传成功: ${localPath} → ${remotePath}`));
                        resolve(true);
                    }
                });
            });
        });
    }

    async deleteFile(remotePath, retryCount = 0) {
        const MAX_RETRIES = 2;
        
        if (!this.isConnected) {
            const connected = await this.connect();
            if (!connected) return false;
        }

        return new Promise((resolve, reject) => {
            const timeout = setTimeout(() => {
                console.error(chalk.red(`删除超时 ${remotePath}`));
                this.isConnected = false;
                resolve(false);
            }, 30000);

            this.client.delete(remotePath, (err) => {
                clearTimeout(timeout);
                
                if (err) {
                    console.error(chalk.red(`删除失败 ${remotePath}:`), err.message);
                    
                    if (retryCount < MAX_RETRIES && (err.message.includes('ETIMEDOUT') || err.message.includes('connect'))) {
                        console.log(chalk.yellow(`正在重试删除 ${remotePath} (${retryCount + 1}/${MAX_RETRIES})...`));
                        this.isConnected = false;
                        setTimeout(() => {
                            this.deleteFile(remotePath, retryCount + 1)
                                .then(resolve)
                                .catch(() => resolve(false));
                        }, 2000);
                        return;
                    }
                    
                    resolve(false);
                } else {
                    console.log(chalk.yellow(`✓ 删除成功: ${remotePath}`));
                    resolve(true);
                }
            });
        });
    }

    async syncDirectory(localDir, remoteDir, options = {}) {
        const { exclude = [] } = options;
        
        if (!fs.existsSync(localDir)) {
            console.error(chalk.red(`本地目录不存在: ${localDir}`));
            return false;
        }

        // 使用多次重试连接，自动尝试不同模式
        let connected = false;
        const modes = [null, true, false]; // null=默认，true=被动，false=主动
        
        for (const mode of modes) {
            if (!this.isConnected) {
                connected = await this.connect(mode);
                if (connected) break;
            } else {
                connected = true;
                break;
            }
        }
        
        if (!connected) {
            console.error(chalk.red('无法连接到 FTP 服务器'));
            return false;
        }

        try {
            console.log(chalk.blue(`开始同步目录: ${localDir} → ${remoteDir}`));
            await this.syncDirectoryRecursive(localDir, remoteDir, exclude);
            console.log(chalk.green(`✓ 目录同步完成: ${remoteDir}`));
            return true;
        } catch (error) {
            console.error(chalk.red(`目录同步失败:`), error.message);
            return false;
        }
    }

    async syncDirectoryRecursive(localDir, remoteDir, exclude) {
        const items = fs.readdirSync(localDir);
        
        await this.ensureDir(remoteDir);

        for (const item of items) {
            if (this.shouldExclude(item, exclude)) {
                continue;
            }

            const localPath = path.join(localDir, item);
            const remotePath = remoteDir + '/' + item;
            const stats = fs.statSync(localPath);

            if (stats.isDirectory()) {
                await this.syncDirectoryRecursive(localPath, remotePath, exclude);
            } else if (stats.isFile()) {
                await this.uploadFile(localPath, remotePath);
            }
        }
    }

    shouldExclude(filename, excludePatterns) {
        return excludePatterns.some(pattern => {
            if (pattern.includes('*')) {
                const regex = new RegExp(pattern.replace(/\*/g, '.*'));
                return regex.test(filename);
            }
            return filename === pattern || filename.includes(pattern);
        });
    }

    getRelativePath(filePath, basePath) {
        return path.relative(basePath, filePath).replace(/\\/g, '/');
    }

    async testConnection() {
        try {
            await this.connect();
            
            return new Promise((resolve, reject) => {
                this.client.pwd((err, currentDir) => {
                    if (err) {
                        reject(err);
                    } else {
                        console.log(chalk.green(`✓ 当前目录: ${currentDir}`));
                        resolve(true);
                    }
                });
            });
        } catch (error) {
            throw error;
        }
    }
}

module.exports = FTPClient;